#include "pch.hpp"
#include "D3D11ShaderConstant.hpp"
#include "D3D11Exception.hpp"

using namespace Engine;
using namespace Engine::Video;




D3D11ShaderConstant::D3D11ShaderConstant(ID3D11ShaderReflectionConstantBuffer* buffer, const D3D11_SHADER_BUFFER_DESC& desc, uint32 index)
	: m_index(index)
	, m_reflection(desc.Size, desc.Variables)
{
	std::size_t offset = 0;

	for(uint32 o = 0; o < desc.Variables; ++o)
	{
		ID3D11ShaderReflectionVariable* variable = buffer->GetVariableByIndex(o);
		D3D11_SHADER_VARIABLE_DESC desc;
		D3D11_API(variable->GetDesc(&desc));
		ID3D11ShaderReflectionType* type = variable->GetType();
		D3D11_SHADER_TYPE_DESC tdesc;
		D3D11_API(type->GetDesc(&tdesc));

		if(desc.StartOffset != offset)
			BOOSTEXT_THROW(exception(format("Unexpected offset in shader constant '%1%': Actual offset %2% but shader reports %3%") % desc.Name % offset % desc.StartOffset));

		// Depending on the type, we have to insert multiple layouts
		// to stay consistent with vertexbuffer layouts (ie a matrix
		// consists of 4 rows, each R32G32B32A32_FLOAT...)
		switch(tdesc.Class)
		{
		case D3D10_SVC_MATRIX_COLUMNS:
			for(int i = 0; i < 4; ++i)
			{
				ReflectionElement element;
				element.semanticName  = desc.Name;
				element.semanticIndex = i;
				element.relativeOffset        = 0;
				element.format        = Format::R32G32B32A32_FLOAT;
				m_reflection.add(element);

				offset += Format::size(element.format);
			}
			break;
		case D3D10_SVC_MATRIX_ROWS:
			BOOSTEXT_THROW(exception("Row major matrices are not yet supported"));
		case D3D10_SVC_OBJECT:
			BOOSTEXT_THROW(exception("Objects are not yet supported"));
		case D3D10_SVC_STRUCT:
			BOOSTEXT_THROW(exception("Structures are not yet supported"));
		case D3D11_SVC_INTERFACE_CLASS:
			BOOSTEXT_THROW(exception("Classes are not yet supported"));
		case D3D11_SVC_INTERFACE_POINTER:
			BOOSTEXT_THROW(exception("Interfaces are not yet supported"));
		case D3D10_SVC_SCALAR:
		case D3D10_SVC_VECTOR:
			{
				ReflectionElement element;
				element.semanticName  = desc.Name;
				element.relativeOffset        = 0;
				element.semanticIndex = 0;
				element.format        = fromClass(tdesc.Class);
				m_reflection.add(element);

				offset += Format::size(element.format);
			}
			break;
		default:
			BOOSTEXT_THROW(invalid_value_exception(tdesc.Class));
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void D3D11ShaderConstant::compare(const StaticReflection& reflection)
{
	if(m_staticReflection && *m_staticReflection != reflection)
		BOOSTEXT_THROW(exception("This shader constant buffer uses another format"));

	if(m_reflection != reflection)
		BOOSTEXT_THROW(exception("This shader constant buffer uses another format"));

	m_staticReflection = reflection;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

uint32 D3D11ShaderConstant::index() const
{
	return m_index;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Format::Type D3D11ShaderConstant::fromClass(D3D10_SHADER_VARIABLE_CLASS c)
{
	switch(c)
	{
	case D3D10_SVC_SCALAR: return Format::R32_FLOAT;
	case D3D10_SVC_VECTOR: return Format::R32G32B32A32_FLOAT;
	default: BOOSTEXT_THROW(invalid_value_exception(c));
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
